package org.vaadin.nf4v.ext.runtime.menu.tree;

import com.vaadin.event.ItemClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.Tree;
import org.vaadin.nf4v.core.NavigableApplication;
import org.vaadin.nf4v.core.NavigationException;
import org.vaadin.nf4v.core.config.ContextConfig;
import org.vaadin.nf4v.core.config.PageConfig;

/**
 * Utility which provides basic menu tree building functionality (according to @Menu annotations).
 * It just redirect calls to default menu tree builder implementation.
 * <p/>
 * If default menu building logic should be extended, extend MenuTreeBuilderImpl and use it directly
 * or make utility class like that.
 */
public class MenuTreeBuilder {
    private static final HierarchicalContainerBuilderImpl builder = new HierarchicalContainerBuilderImpl();

    /**
     * Builds entire application menu (including all contexts).
     * in other way automatic context switching will not work (framework will try to find page in current context).
     * Note: includeContextNodes affects only if window classes were also annotated with @Menu annotation
     *
     * @param includeContextNodes true to include context nodes in menu building
     * @return menu bar
     */
    public static Tree buildFullMenuTree(boolean includeContextNodes) {
        Tree tree = buildEmptyTree();
        tree.setContainerDataSource(
                builder.buildFullMenuTree(NavigableApplication.getCurrent().getNavigationConfig(), includeContextNodes)
        );
        tree.setItemCaptionPropertyId(ItemProperties.NAME);
        tree.setItemIconPropertyId(ItemProperties.ICON);
        return tree;
    }

    /**
     * Builds menu tree for context.
     * Note: includeContextNode affects only if window class were also annotated with @Menu annotation
     *
     * @param context            context name
     * @param includeContextNode true to include context node in menu building
     * @return menu bar
     * @throws org.vaadin.nf4v.core.NavigationException
     *          if context with provided name is not found
     */
    public static Tree buildContextMenuTree(String context, boolean includeContextNode) throws NavigationException {
        Tree tree = buildEmptyTree();
        ContextConfig config = NavigableApplication.getCurrent().getNavigationConfig().getContextConfig(context);
        tree.setContainerDataSource(builder.buildContextMenuTree(config, includeContextNode));
        tree.setItemCaptionPropertyId(ItemProperties.NAME);
        tree.setItemIconPropertyId(ItemProperties.ICON);
        return tree;
    }

    /**
     * Builds menu tree for current page
     *
     * @param context           context name
     * @param pageClass         page class
     * @param includingThisPage true to include page itself as root menu item
     * @return menu bar
     * @throws NavigationException if context or page not found
     */
    public static Tree buildPageMenuTree(String context, Class<? extends Component> pageClass, boolean includingThisPage)
            throws NavigationException {
        Tree tree = buildEmptyTree();
        PageConfig config = NavigableApplication.getCurrent().getNavigationConfig().getPageConfig(context, pageClass);
        tree.setContainerDataSource(builder.buildPageMenuTree(config, includingThisPage));
        tree.setItemCaptionPropertyId(ItemProperties.NAME);
        tree.setItemIconPropertyId(ItemProperties.ICON);
        return tree;
    }

    private static Tree buildEmptyTree() {
        Tree tree = new Tree();
        tree.addListener(new ItemClickEvent.ItemClickListener() {
            @Override
            public void itemClick(ItemClickEvent event) {
                TreeNavigationCommand command =
                        (TreeNavigationCommand) event.getItem().getItemProperty(ItemProperties.COMMAND).getValue();
                if (command != null) {
                    command.run();
                }
            }
        });
        return tree;
    }

    public static void openTree(Tree tree) {
        for (Object id : tree.rootItemIds()) {
            tree.expandItemsRecursively(id);
        }
    }
}
